/*
 * Created on 2006-8-22
 */
package indexing.grid;


import java.util.*;
import spatial.geometry.*;

/**
 * @author Xiaohui
 * Cells in a grid index should be subclass of rectangles.
 * Assume cells are squares for simplicity.
 * A group of squares are created in grids.
 */
public class Cell extends Rectangle{
	private CellID cellID;
	private double cellSize;

	public Cell(CellID cid, double cellSize,Location lowerLeft) {
		super(lowerLeft, cellSize, cellSize);
		this.cellID = cid;
		this.cellSize = cellSize;
		
	}

	public Location getLowerLeft() {
		double x_left = this.cellID.getColumn_id() * this.cellSize;
		double y_down = this.cellID.getRow_id() * this.cellSize;
		return new Location(x_left, y_down);
	}

	public Location getCenter() {
		double x = this.cellID.getColumn_id() * this.cellSize + this.cellSize
				/ 2;
		double y = this.cellID.getRow_id() * this.cellSize + this.cellSize / 2;
		return new Location(x, y);
	}

	/**
	 * Compute the mininum distance between the cell and the given location.
	 * 
	 * @param location
	 * @return min-distance from the cell to the location.
	 */
	public double minDistanceTo(Location location) {
		/**
		 * <pre>
		 * if in the cell, then min-distance = 0 
		 * if left, then min-distance = cell.left_x - location.x 
		 * if right, then min-distance = location.x - cell.right_x 
		 * if up, then = location.y - cell.up_y 
		 * if down, then = cell.down_y - location_y 
		 * otherwise, Min(distance to four corners)
		 * </pre>
		 */
		double x_left = this.cellID.getColumn_id() * this.cellSize;
		double x_right = x_left + this.cellSize;
		double y_down = this.cellID.getRow_id() * this.cellSize;
		double y_up = y_down + this.cellSize;

		if (x_left > location.getX()) {// left
			if (y_down > location.getY()) {// down
				Location left_down = new Location(x_left, y_down);
				return location.distance(left_down);
			} else if (y_up < location.getY()) {// up
				Location left_up = new Location(x_left, y_up);
				return location.distance(left_up);
			} else {// straight
				return x_left - location.getX();
			}

		} else if (x_right < location.getX()) {// right
			if (y_down > location.getY()) {// down
				Location right_down = new Location(x_right, y_down);
				return location.distance(right_down);
			} else if (y_up < location.getY()) {// up
				Location right_up = new Location(x_right, y_up);
				return location.distance(right_up);
			} else {// straight
				return location.getX() - x_right;
			}

		} else {// vertical
			if (y_down > location.getY()) {// down
				return y_down - location.getY();
			} else if (y_up < location.getY()) {// up
				return location.getY() - y_up;
			} else {// in
				return 0.0;
			}
		}
	}

	/**
	 * Compute the maximum distance from the cell to the given location.
	 * 
	 * @param location
	 * @return max-distance from the cell to the location.
	 */
	public double maxDistanceTo(Location location) {
		/*
		 * Max(distances to 4 corners)
		 */
		double x_left = this.cellID.getColumn_id() * this.cellSize;
		double x_right = x_left + this.cellSize;
		double y_down = this.cellID.getRow_id() * this.cellSize;
		double y_up = y_down + this.cellSize;
		Location left_down = new Location(x_left, y_down);
		Location left_up = new Location(x_left, y_up);
		Location right_down = new Location(x_right, y_down);
		Location right_up = new Location(x_right, y_up);
		return Math.max(Math.max(location.distance(left_down), location
				.distance(left_up)), Math.max(location.distance(right_down),
				location.distance(right_up)));
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof Cell) {
			Cell acell = (Cell) obj;
			return this.cellID.equals(acell.cellID);
		}
		return false;
	}

	@Override
	public int hashCode() {
		return this.cellID.hashCode();
	}

	public CellID getCellID() {
		return cellID;
	}

	public double getCellSize() {
		return cellSize;
	}

}
